function TableManager() {
}

TableManager.prototype = {
	// Initialization
	
	initialize: function(tableDivId, messages, options) {
		this.tableDivId = tableDivId;
		this.tableConfiguration = this.createTableConfiguration();
		this.messages = messages;
		this.options = options;
		this.listenerTypes = {};
		this.onInitialize();
	},
	
	onInitialize: function() {
	},

	// Global

	showLoadingImage: function() {
		jQuery("#loading").show();
	},

	hideLoadingImage: function() {
		jQuery("#loading").hide();
	},

	// Common
	
	getElementId: function(element) {
		throw new Error("This method must be implemented...");
	},
	
	createEmptyElement: function() {
		throw new Error("This method must be implemented...");
	},
	
	getOptionValue: function(entryName, defaultValue) {
		var optionValue = this.options[entryName];
		if (optionValue==null) {
			optionValue = defaultValue;
		}
		return optionValue;
	},

	getPatternName: function() {
		return this.getOptionValue("pattern", "pattern");
	},

	getIdSuffixForNewElement: function() {
		return this.getOptionValue("idSuffixForNewElement", "New");
	},

	getLineAddDivId: function() {
		return this.getOptionValue("lineAddDivId", "lineAdd");
	},

	getLineValidateDivId: function() {
		return this.getOptionValue("lineValidateDivId", "lineValidate");
	},

	getLineCancelDivId: function() {
		return this.getOptionValue("lineCancelDivId", "lineCancel");
	},

	getLineEditDivId: function() {
		return this.getOptionValue("lineEditDivId", "lineEdit");
	},

	getLineDeleteDivId: function() {
		return this.getOptionValue("lineDeleteDivId", "lineDelete");
	},

	getMessage: function(messageName) {
		var message = this.messages[messageName];
		if (message==null) {
			message = "###"+messageName+"###";
		}
		return message;
	},

	clonePattern: function(id) {
		throw new Error("This method must be implemented...");
	},

	removeAllRows: function(tableDivId, pattern) {
		throw new Error("This method must be implemented...");
	},

	// CRUD operations

	getLoadElementsFunction: function() {
		throw new Error("This method must be implemented...");
	},
	
	getLoadElementsFunctionParameters: function() {
		return [];
	},
	
	getAddElementFunction: function() {
		throw new Error("This method must be implemented...");
	},
	
	getStoreElementFunction: function() {
		throw new Error("This method must be implemented...");
	},
	
	getDeleteElementFunction: function() {
		throw new Error("This method must be implemented...");
	},
	
	// Table

	createTableConfiguration: function() {
		throw new Error("This method must be implemented...");
	},

	createBinderForElement: function(element) {
		throw new Error("This method must be implemented...");
	},

	createDefaultDisplayCreator: function() {
		var currentThis = this;
		return function(element, fieldName, fieldValue) {
			/*var id = currentThis.getElementId(element);
			jQuery("#"+fieldName+id).html(fieldValue);*/
			return fieldValue;
		};
	},

	createDefaultEditCreator: function() {
		var currentThis = this;
		return function(element, fieldName, fieldValue) {
			if (element==null) {
				var id = currentThis.getIdSuffixForNewElement();
				//jQuery("#"+fieldName+id).html("<input type=\"text\" id=\""+fieldName+"Field"+id+"\" value=\"\"/>");
				return "<input type=\"text\" id=\""+fieldName+"Field"+id+"\" value=\"\"/>";
			} else {
				var id = currentThis.getElementId(element);
				//jQuery("#"+fieldName+id).html("<input type=\"text\" id=\""+fieldName+"Field"+id+"\" value=\""+fieldValue+"\"/>");
				return "<input type=\"text\" id=\""+fieldName+"Field"+id+"\" value=\""+fieldValue+"\"/>";
			}
		};
	},
	
	createDefaultTableEntryConfiguration: function() {
		var defaultDisplayCreator = this.createDefaultDisplayCreator();
		var defaultEditCreator = this.createDefaultEditCreator();
		return {
			"displayCreator": defaultDisplayCreator,
			"editCreator": defaultEditCreator
		};
	},
	
	createDefaultBinderForElement: function() {
		return {
			getValueForField: function(fieldName, id) {
				var value = jQuery("#"+fieldName+"Field"+id).val();
				return value;
			}
		};
	},

	initializeTableListener: function() {
		var currentThis = this;
		var addDiv = this.getLineAddDivId();
		jQuery("#"+addDiv).click(function() {
			currentThis.addEmptyCategoryLine();
		});
	},
	
	triggerListeners: function(type, element, id) {
		var listeners = this.listenerTypes[type];
		if (listeners!=null) {
			for (var cpt=0; cpt<listeners.length; cpt++) {
				listeners[cpt].onEvent(element, id);
			}
		}
	},
	
	registerListener: function(type, listener) {
		var listeners = this.listenerTypes[type];
		if (listeners==null) {
			listeners = [];
			this.listenerTypes[type] = listeners;
		}
		
		listeners.push(listener);
	},
	
	loadElements: function() {
		var currentThis = this;
		this.showLoadingImage();

		var callback = function(elements) {
			currentThis.removeAllRows(currentThis.tableDivId, currentThis.getPatternName());
			for (var i = 0; i < elements.length; i++) {
				var element = elements[i];
				currentThis.addElementLine(element);
			}
			currentThis.hideLoadingImage();
		};
		var parameters = this.getLoadElementsFunctionParameters();
		if (parameters.length==0) {
			this.getLoadElementsFunction()(callback);
		} else {
			var allParameters = [];
			allParameters = allParameters.concat(parameters);
			allParameters.push(callback);
			this.getLoadElementsFunction().apply(null,allParameters);
		}
	},
	
	addElementLine: function(element) {
		var id = this.getElementId(element);
		this.clonePattern(id);
		
		var tableConfiguration = this.tableConfiguration;
		for (var fieldName in tableConfiguration) {
			var tableConfigurationForField = tableConfiguration[fieldName];
			if (tableConfigurationForField==null) {
				continue;
			}

			var fieldValue = element[fieldName];
			var fieldCreator = tableConfigurationForField["displayCreator"];
			if (fieldCreator==null) {
				fieldCreator = function(element, fieldName, fieldValue) {
					return fieldValue;
				};
			}
			var field = fieldCreator(element, fieldName, fieldValue, this.options);
			if (field!=null) {
				jQuery("#"+fieldName+id).html(field);
			}
		}

		this.triggerListeners("onLineAdded", element, id);

		jQuery("#"+this.getLineValidateDivId()+id).hide();
		jQuery("#"+this.getLineCancelDivId()+id).hide();
		jQuery("#"+this.getPatternName()+id).hide().fadeIn();
		this.addElementFieldListener(element);
	},
	
	addEmptyCategoryLine: function() {
		var id = this.getIdSuffixForNewElement();
		this.clonePattern(id);

		var tableConfiguration = this.tableConfiguration;
		for (var fieldName in tableConfiguration) {
			var tableConfigurationForField = tableConfiguration[fieldName];
			if (tableConfigurationForField==null || fieldName=="new") {
				continue;
			}

			var fieldCreator = tableConfigurationForField["editCreator"];
			if (fieldCreator==null) {
				fieldCreator = function(element, fieldName, fieldValue) {
					return "";
				};
			}
			var field = fieldCreator(null, fieldName, null, this.options);
			jQuery("#"+fieldName+id).html(field);
		}

		jQuery("#"+this.getPatternName()+id).hide().fadeIn();
		jQuery("#"+this.getLineEditDivId()+id).hide();
		jQuery("#"+this.getLineDeleteDivId()+id).hide();
		jQuery("#"+this.getLineValidateDivId()+id).show();
		jQuery("#"+this.getLineCancelDivId()+id).show();

		this.addElementValidateFieldListenerForAdding();
	},

	addElementFieldListener: function(element) {
		var currentThis = this;
		var id = this.getElementId(element);
		jQuery("#"+this.getLineEditDivId()+id).click(function() {
			var tableConfiguration = currentThis.tableConfiguration;
			for (var fieldName in tableConfiguration) {
				var tableConfigurationForField = tableConfiguration[fieldName];
				if (tableConfigurationForField==null || fieldName=="new") {
					continue;
				}

				var fieldValue = element[fieldName];
				var fieldCreator = tableConfigurationForField["editCreator"];
				if (fieldCreator==null) {
					fieldCreator = function(element, fieldName, fieldValue) {
						return fieldValue;
					};
				}
				var field = fieldCreator(element, fieldName, fieldValue, currentThis.options);
				jQuery("#"+fieldName+id).html(field);
			}

			jQuery("#"+currentThis.getLineEditDivId()+id).hide();
			jQuery("#"+currentThis.getLineDeleteDivId()+id).hide();
			jQuery("#"+currentThis.getLineValidateDivId()+id).show();
			jQuery("#"+currentThis.getLineCancelDivId()+id).show();

			currentThis.addElementValidateFieldListener(element);
		});

		jQuery("#"+this.getLineDeleteDivId()+id).click(function() {
			var removeMessage = currentThis.getMessage("remove.element.message");
			var answer = confirm(removeMessage);
			if (answer) {
				currentThis.showLoadingImage();
				currentThis.getDeleteElementFunction()(element, function() {
					var id = currentThis.getElementId(element);
					jQuery("#"+currentThis.getPatternName()+id).remove();
					currentThis.hideLoadingImage();
				});
			}
		});
	},

	addElementValidateFieldListener: function(element) {
		var currentThis = this;
		var id = this.getElementId(element);
		jQuery("#"+this.getLineValidateDivId()+id).click(function() {
			var tableConfiguration = currentThis.tableConfiguration;
			var binder = currentThis.createBinderForElement(element);
			for (var fieldName in element) {
				var tableConfigurationForField = tableConfiguration[fieldName];
				if (tableConfigurationForField==null) {
					continue;
				}

				element[fieldName] = binder.getValueForField(fieldName, id);
			}

			currentThis.showLoadingImage();
			currentThis.getStoreElementFunction()(element, function() {
				for (var fieldName in tableConfiguration) {
					var tableConfigurationForField = tableConfiguration[fieldName];
					if (tableConfigurationForField==null || fieldName=="new") {
						continue;
					}

					var fieldValue = element[fieldName];
					var fieldCreator = tableConfigurationForField["displayCreator"];
					if (fieldCreator==null) {
						fieldCreator = function(element, fieldName, fieldValue) {
							return "";
						};
					}
					var field = fieldCreator(element, fieldName, fieldValue, currentThis.options);
					jQuery("#"+fieldName+id).html(field);
				}

				jQuery("#"+currentThis.getLineEditDivId()+id).show();
				jQuery("#"+currentThis.getLineDeleteDivId()+id).show();
				jQuery("#"+currentThis.getLineValidateDivId()+id).hide();
				jQuery("#"+currentThis.getLineCancelDivId()+id).hide();
				currentThis.hideLoadingImage();
			});
		});
		jQuery("#"+this.getLineCancelDivId()+id).click(function() {
			var tableConfiguration = currentThis.tableConfiguration;
			for (var fieldName in element) {
				var tableConfigurationForField = tableConfiguration[fieldName];
				if (tableConfigurationForField==null) {
					continue;
				}

				var fieldValue = element[fieldName];
				var fieldCreator = tableConfigurationForField["displayCreator"];
				if (fieldCreator==null) {
					fieldCreator = function(element, fieldName) {
						return "";
					};
				}
				var field = fieldCreator(element, fieldName, fieldValue, currentThis.options);
				jQuery("#"+fieldName+id).html(field);
			}

			jQuery("#"+currentThis.getLineEditDivId()+id).show();
			jQuery("#"+currentThis.getLineDeleteDivId()+id).show();
			jQuery("#"+currentThis.getLineValidateDivId()+id).hide();
			jQuery("#"+currentThis.getLineCancelDivId()+id).hide();
		});
	},

	addElementValidateFieldListenerForAdding: function() {
		var currentThis = this;
		var id = this.getIdSuffixForNewElement();
		jQuery("#"+this.getLineValidateDivId()+id).click(function() {
			var element = currentThis.createEmptyElement();
			var binder = currentThis.createBinderForElement(element);
			var tableConfiguration = currentThis.tableConfiguration;
			for (var fieldName in element) {
				var tableConfigurationForField = tableConfiguration[fieldName];
				if (tableConfigurationForField==null) {
					continue;
				}

				element[fieldName] = binder.getValueForField(fieldName, id);
			}

			currentThis.showLoadingImage();
			currentThis.getAddElementFunction()(element, function() {
				//TODO: check if reload or not
				currentThis.loadElements();
				currentThis.hideLoadingImage();
			});
		});
		jQuery("#"+this.getLineCancelDivId()+id).click(function() {
			jQuery("#"+currentThis.getPatternName()+id).remove();
		});
	}

};